<?php
//========================================================================
// Author:  Pascal KISSIAN
// Resume:  http://pascal.kissian.net
//
// Copyright (c) 2015-2018 Pascal KISSIAN
//
// Published under the MIT License
//          Consider it as a proof of concept!
//          No warranty of any kind.
//          Use and abuse at your own risks.
//========================================================================

use PhpParser\Node;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Do_;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\Goto_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Label;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\While_;

class MyReverseNodeVisitor extends PhpParser\NodeVisitorAbstract       // all parsing and replacement of scrambled names is done here!  反混淆解析
{                                                               // see PHP-Parser for documentation!
    private $t_loop_stack = array();
    private $current_class_name = null;
    private $is_in_class_const_definition = false;

    private function reverse_shuffle_stmts(PhpParser\Node &$node)//重新洗牌  子节点重新洗牌  反混淆了语句块
    {
        global $conf;
        if ($conf->shuffle_stmts) {
            if (isset($node->stmts))//节点有子节点
            {

                $stmts = $node->stmts;
                $chunk_size = shuffle_get_chunk_size($stmts);
                if ($chunk_size <= 0) return false; // should never occur!

                if (count($stmts) > (2 * $chunk_size)) {
                    //    $last_inst      = array_pop($stmts);
                    $stmts = reverse_shuffle_statements($stmts);//重新洗牌
                    //    $stmts[]        = $last_inst;
                    $node->stmts = $stmts;
                    return true;
                }
            }
        }
        return false;
    }



    private function get_identifier_name(PhpParser\Node $node)
    {
        if ($node instanceof PhpParser\Node\Identifier || $node instanceof PhpParser\Node\VarLikeIdentifier) return $node->name;
        return '';
    }

    private function set_identifier_name(PhpParser\Node &$node, $name)
    {
        if ($node instanceof PhpParser\Node\Identifier || $node instanceof PhpParser\Node\VarLikeIdentifier) {
            $node->name = $name;
        }
    }

    public function enterNode(PhpParser\Node $node)
    {
        global $conf;
        global $t_scrambler;

    }

    public function fix_if(PhpParser\Node $node,$i){

        //if节点处理
        $tmp_stmts=array();
        $stmts = $node->stmts;
        $else_stmts = $stmts[$i]->stmts;//else 是源代码的if



        $else_goto_index = 1;
        for ($t = 0; $t < count($else_stmts); $t++) {//找到else跳转
            if ($else_stmts[$t] instanceof PhpParser\Node\Stmt\Goto_) {
                $else_goto_index = $t;
                break;
            }
        }


        if ($else_goto_index != 0) {
            var_dump('解析IF异常 else内第一条语句不是goto，请手动解析');
            $tmp_stmts[] = $stmts[$i];
            return [$tmp_stmts,$i+1];
        }

        $else_goto_label = $else_stmts[$else_goto_index]->name;//else跳转标签
        for ($j = 0; $j < count($stmts); $j++) {
            if ($stmts[$j] instanceof PhpParser\Node\Stmt\Label && $stmts[$j]->name->name == $else_goto_label->name) {//else部分
                $else_goto_index = $j;
                break;
            }

        }




        $if_goto_index = 0;
        for ($j = $i + 1; $j < count($stmts); $j++) {
            if ($stmts[$j] instanceof PhpParser\Node\Stmt\Goto_) {//else部分
                $if_goto_index = $j;
                break;
            }

        }


        if ($j != $i + 1) {
            //不是if之后第一个语句块
            var_dump('解析IF异常 IF内第一条语句不是goto，请手动解析');
            $tmp_stmts[] = $stmts[$i];
            return [$tmp_stmts,$i+1];
        }

        //两条分支走向  goto语句作为标记
        $branch_no1 = array();
        for ($j = $if_goto_index; $j < count($stmts);) {
            if ($stmts[$j] instanceof PhpParser\Node\Stmt\Goto_) {//if之后第一个goto
                $branch_no1[] = $j;
                $find_label = $stmts[$j]->name;//下一个跳转label

                //找到label
                $b_find = false;
                $is_loop=false;
                for ($t = 0; $t < count($stmts); $t++) {
                    if ($stmts[$t] instanceof PhpParser\Node\Stmt\Label && $stmts[$t]->name->name == $find_label->name) {
                        if($stmts[$t]->getAttribute('if_pollute',false)){
                            //进入循环
                            $is_loop=true;
                        }



                        $b_find = true;
                        $j = $t;
                        break;
                    }
                }
                if (!$b_find) {
                    var_dump('没有找到goto对应的label，异常');
                    break;
                }

                if ($is_loop) {
                    var_dump('进入循环，请手动恢复');
                    break;
                }

                //更新$j

            } else {
                $j++;
            }
            $stmts[$t]->setAttribute('if_pollute',true);



            if ($j == count($stmts) - 1) {
                break;
            }


        }


        //第二条分支走向
        $branch_no2 = array();

        for ($j = $else_goto_index; $j < count($stmts);) {
            if ($stmts[$j] instanceof PhpParser\Node\Stmt\Goto_) {//if之后第一个goto
                $branch_no2[] = $j;
                $find_label = $stmts[$j]->name;//下一个跳转label

                //找到label
                $b_find = false;
                $is_loop=false;
                for ($t = 0; $t < count($stmts); $t++) {
                    if ($stmts[$t] instanceof PhpParser\Node\Stmt\Label && $stmts[$t]->name == $find_label) {
                        if(   $stmts[$t]->getAttribute('if_pollute',false)){
                            //进入循环
                            $is_loop=true;
                        }

                        $b_find = true;
                        $j = $t;
                        break;
                    }
                }
                if (!$b_find) {
                    var_dump('没有找到goto对应的label，异常');
                    break;
                }

                if ($is_loop) {
                    var_dump('进入循环，请手动恢复');
                    break;
                }
                //更新$j

            } else {
                $j++;
            }

            $stmts[$t]->setAttribute('if_pollute',true);

            if ($j == count($stmts) - 1) {
                break;
            }


        }
        //找到两条分支交汇点，就是退出if else 块的节点
        $end_if_index = array_intersect($branch_no1, $branch_no2);
        if (count($end_if_index) == 0) {
            $end_if_index=[count($stmts)-1];//分支没有交集 两个方向
            var_dump("两条分支没有交集");


        }



        foreach($end_if_index as $value){
            $end_if_index =$value;
            break;
        }
        var_dump($end_if_index);
            //填充if块
            $if_stmts = array();

            for ($j = $if_goto_index; $j < count($stmts);) {
                if ($stmts[$j] instanceof PhpParser\Node\Stmt\If_) {
                    $if_result= $this->fix_if($node,$j);

                    foreach($if_result[0] as $ifre)  $if_stmts[] = $ifre;
                    $j =$if_result[1];

                }
                else if ($stmts[$j] instanceof PhpParser\Node\Stmt\Goto_) {//if之后第一个goto
                    $find_label = $stmts[$j]->name;//下一个跳转label


                    if ($j == $end_if_index || $j == count($stmts) - 1) {
                        break;//已经到if块结尾
                    }

                    $if_stmts[] = $stmts[$j];//填充代码块
                    //找到label
                    $is_loop=false;
                    $b_find = false;
                    for ($t = 0; $t < count($stmts); $t++) {
                        if ($stmts[$t] instanceof PhpParser\Node\Stmt\Label && $stmts[$t]->name->name == $find_label->name) {

                            if($stmts[$t]->getAttribute('pollute',false)){


                                var_dump('进入loop');

                                //已经跳转过 避免死循环
                                $is_loop =true;
                            }

                            $b_find = true;
                            $j = $t;
                        }
                    }
                    if (!$b_find) {
                        var_dump('没有找到goto对应的label，异常');
                        break;
                    }

                    if ($is_loop) {
                        var_dump('进入循环，请手动恢复');
                        break;
                    }
                    //更新$j



                } else {
                    $if_stmts[] = $stmts[$j];
                    if ($j == count($stmts) - 1) {//到达结尾
                        break;
                    }
                    $j++;
                }
                $stmts[$j]->setAttribute('pollute',true);

            }






            //else 数据块
            $else_stmts = array();
            for ($j = $else_goto_index; $j < count($stmts);) {
                if ($stmts[$j] instanceof PhpParser\Node\Stmt\If_) {
                    $if_result= $this->fix_if($node,$j);

                    foreach($if_result[0] as $ifre)  $else_stmts[] = $ifre;
                    $j =$if_result[1];

                }
                else if ($stmts[$j] instanceof PhpParser\Node\Stmt\Goto_) {//if之后第一个goto
                    $find_label = $stmts[$j]->name;//下一个跳转label



                    if ($j == $end_if_index || $j == count($stmts) - 1) {
                        break;//已经到if块结尾
                    }


                    $else_stmts[] = $stmts[$j];//填充代码块

                    //找到label
                    $is_loop=false;
                    $b_find = false;
                    for ($t = 0; $t < count($stmts); $t++) {
                        if ($stmts[$t] instanceof PhpParser\Node\Stmt\Label && $stmts[$t]->name == $find_label) {
                            if($stmts[$t]->getAttribute('pollute',false)){


                                var_dump('进入loop');

                                //已经跳转过 避免死循环
                                $is_loop =true;
                            }
                            $b_find = true;
                            $j = $t;
                        }
                    }
                    if (!$b_find) {
                        var_dump('没有找到goto对应的label，异常');
                        break;
                    }

                    if ($is_loop) {
                        var_dump('进入循环，请手动恢复');
                        break;
                    }
                    //更新$j



                } else {
                    $else_stmts[] = $stmts[$j];
                    if ($j == count($stmts) - 1) {//到达结尾
                        break;
                    }
                    $j++;
                }
                $stmts[$j]->setAttribute('pollute',true);

            }
            //组合if else 数据块
            $condition =$stmts[$i]->cond;
            $new_condition      = new PhpParser\Node\Expr\BooleanNot($stmts[$i]->cond);
            $stmts[$i]->cond=$new_condition;
            $stmts[$i]->stmts = $if_stmts;
            $stmts[$i]->{'else'}= new PhpParser\Node\Stmt\Else_();
            $stmts[$i]->{'else'}->stmts =$else_stmts;



            //查找if else 数据块是否嵌套

        // $this->fix_if_statement($stmts[$i],true);





        //递归处理if 语句

        //$this->fix_if_statement($stmts[$i]->else,true);
        // $this->fix_if_statement($stmts[$i],true);
        //end

        $stmts[$i]->setAttribute('pollute',true);//遍历过
        $tmp_stmts[] = $stmts[$i];
        $i = $end_if_index;


        return [$tmp_stmts,$i];
        //其他可能的跳转 未处理的数据块，放最后面

    }


    public function fix_if_statement(PhpParser\Node $node,$twice=false)
    {
        global $conf;

        if($twice){
           var_dump('二次解析if');
           var_dump(count($node->stmts));
        }

        if (isset($node->stmts)) {

            $stmts = $node->stmts;
            $tmp_stmts = array();
            $goto_index = 0;
            for ($i = 0; $i < count($stmts);) {
                //标签 如果是
                $stmts[$i]->setAttribute('index',$i);

                if ($stmts[$i] instanceof PhpParser\Node\Stmt\If_) {
                    var_dump('进入if');
                    $result =$this->fix_if($node,$i);
                    var_dump($result[1]);


                    foreach($result[0] as $value){
                        $tmp_stmts[] = $value;
                    }

                    $i = $result[1];

                    //其他可能的跳转 未处理的数据块，放最后面



                }
                else if ($stmts[$i] instanceof PhpParser\Node\Stmt\Label) {
                    $tmp_stmts[] = $stmts[$i];

                    if($i-1>0&&$stmts[$i-1] instanceof PhpParser\Node\Stmt\Label){
                        if(!$stmts[$i-1]->getAttribute('pollute',false)){
                          //  $tmp_stmts[] = $stmts[$i-1];
                            $stmts[$i-1]->setAttribute('pollute',true);//遍历过
                        }

                    }
                    if($i-2>0&&$stmts[$i-2] instanceof PhpParser\Node\Stmt\Label){

                        if(!$stmts[$i-2]->getAttribute('pollute',false)){
                           // $tmp_stmts[] = $stmts[$i-2];
                            $stmts[$i-2]->setAttribute('pollute',true);//遍历过
                        }
                    }

                    if ($i == count($stmts) - 1) {
                        //最后一个label 退出
                        var_dump('最后一个label 退出');
                        break;
                    }
                    $i++;
                    $stmts[$i]->setAttribute('pollute',true);//遍历过
                } //如果是goto
                else if ($stmts[$i] instanceof PhpParser\Node\Stmt\Goto_) {
                    $goto_index = $i;
                    $goto = $stmts[$goto_index];
                    $label_name_next = $goto->name;//下一个要查找的标签
                    $tmp_stmts[] = $stmts[$i];

                  //  var_dump($label_name_next->name);
                    $found = false;
                    $is_loop =false;


                    for ($ts = 0; $ts < count($stmts); ++$ts)//从头查找
                    {

                        if ($stmts[$ts] instanceof PhpParser\Node\Stmt\Label) {


                            if ($stmts[$ts]->name->name == $label_name_next->name) {


                                if($stmts[$ts]->getAttribute('pollute',false)){


                                        var_dump('进入loop');

                                    //已经跳转过 避免死循环
                                    $is_loop =true;
                                }
                                $i = $ts;
                                $found = true;
                                break;
                            }
                        }
                    }


                    if (!$found) {//没有找到goto跳转标签

                        var_dump('没有找到goto跳转标签');
                        break;

                    }

                    if ($is_loop) {//没有找到goto跳转标签
                        var_dump('循环条件进入，请手动恢复');
                        break;

                    }

                    $stmts[$i]->setAttribute('pollute',true);//遍历过

                } //循环遍历是否有if

                else if ($stmts[$i] instanceof PhpParser\Node\Stmt\Return_) {
                    //提前结束
                    $tmp_stmts[] = $stmts[$i];
                    break;//主分支结束
                } else {
                    //默认添加代码
                    $tmp_stmts[] = $stmts[$i];
                    $stmts[$i]->setAttribute('pollute',true);//遍历过
                    $i++;

                }


            }


            $node->stmts = $tmp_stmts;//更新
            unset($tmp_stmts);

            if (!$conf->raw_code) {
                $tmp=array();
                if(isset($node->stmts))
                {
                    for($i=0;$i<count($node->stmts);$i++)
                    {
                        if($node->stmts[$i] instanceof PhpParser\Node\Stmt\Goto_){
                            if($i+1<count($node->stmts)&&$node->stmts[$i+1] instanceof PhpParser\Node\Stmt\Label){

                                if($node->stmts[$i]->name->name==$node->stmts[$i+1]->name->name){
                                    $i++;
                                    continue;
                                }
                            }

                        }

                        $tmp[]=$node->stmts[$i];


                    }
                    $node->stmts=$tmp;
                    unset($tmp);
                }

            }

            unset($stmts);

        }


    }

    public function leaveNode(PhpParser\Node $node)
    {
        global $conf;


        $node_modified = false;


        if ($conf->obfuscate_if_statement)                  // if else elseif   are replaced by goto ...
        {
            $this->fix_if_statement($node);

        }


        if (!$conf->raw_code) {
            $tmp=array();
            if(isset($node->stmts))
            {
                for($i=0;$i<count($node->stmts);$i++)
                {
                    if($node->stmts[$i] instanceof PhpParser\Node\Stmt\Goto_){
                        if($i+1<count($node->stmts)&&$node->stmts[$i+1] instanceof PhpParser\Node\Stmt\Label){

                            if($node->stmts[$i]->name->name==$node->stmts[$i+1]->name->name){
                                $i++;
                                continue;
                            }
                        }

                    }

                    $tmp[]=$node->stmts[$i];


                }
                $node->stmts=$tmp;
            }
           unset($tmp);
        }


        if ($node_modified) return $node;
    }
}

?>
